package com.bqjr.cloud.zuul.ribbon;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.concurrent.ConcurrentHashMap;

import javax.servlet.http.HttpServletRequest;

import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.cloud.netflix.zuul.filters.support.FilterConstants;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.stereotype.Component;

import com.bqjr.cloud.zuul.constant.RouteConstant;
import com.bqjr.cloud.zuul.model.ServerInfo;
import com.bqjr.cloud.zuul.model.ServiceInfo;
import com.bqjr.cloud.zuul.model.UserInfo;
import com.bqjr.cloud.zuul.service.ServerInfoService;
import com.bqjr.cloud.zuul.service.ServiceInfoService;
import com.bqjr.cloud.zuul.service.UserInfoService;
import com.netflix.zuul.context.RequestContext;

@Component
public class RouteManager implements ApplicationListener<ContextRefreshedEvent> {
	
	private static final Logger logger = LoggerFactory.getLogger(RouteManager.class);
	private static final Map<String, List<ServerInfo>> serviceInfoMap = new ConcurrentHashMap<>();
	private static final Random random = new Random();
	private static volatile boolean isStarted = false;
	
	@Autowired
	private ServiceInfoService serviceInfoService;
	@Autowired
	private ServerInfoService serverInfoService;
	@Autowired
	private UserInfoService userInfoService;
	@Autowired
	private DiscoveryClient discoveryClient;
	
	public void onApplicationEvent(ContextRefreshedEvent event) {
		if (!isStarted) {
			isStarted = true;
			Thread thread = new Thread(new Runnable() {
				@Override
				public void run() {
					while(true) {
						try {
							sync();
							load();
						} catch (Exception e) {
							logger.error("RouteManager load error", e);
						} finally {
							try {
								Thread.sleep(60000);
							} catch (InterruptedException e) {
							}
						}
					}
				}
			});
			thread.setDaemon(true);
			thread.start();
		}
	}
	
	private void load() {
		List<ServiceInfo> serviceInfoList = serviceInfoService.getAll();
		for (ServiceInfo serviceInfo : serviceInfoList) {
			List<ServerInfo> serverInfoList = serverInfoService.getByServiceId(serviceInfo.getId());
			serviceInfoMap.put(serviceInfo.getId(), serverInfoList);
		}
	}
	
	private void sync() {
		//找出当前保存的所有服务信息
		List<ServiceInfo> serviceInfoList = serviceInfoService.getAll();
		Map<String, ServiceInfo> map = new HashMap<>();
		for(ServiceInfo info : serviceInfoList) {
			map.put(info.getId(), info);
		}
		//从注册中心获取所有服务名
		List<String> list = discoveryClient.getServices();
		for (String serviceId : list) {
			ServiceInfo serviceInfo = map.get(serviceId);
			if (serviceInfo != null) {
				map.remove(serviceId);
				if (!serviceInfo.getIsEnabled()) {
					serviceInfo.setIsEnabled(true);
					serviceInfoService.update(serviceInfo);
				}
			} else {
				//创建服务信息
				try {
					serviceInfo = new ServiceInfo();
					serviceInfo.setId(serviceId);
					serviceInfo.setIsEnabled(true);
					serviceInfoService.insert(serviceInfo);
				} catch (Exception e) {
					logger.error("Insert ServiceInfo Error", e);
				}
			}
			//检查服务器
			List<ServerInfo> matchList = new ArrayList<>();
			List<ServerInfo> servInfoList = serverInfoService.getByServiceId(serviceId);
			List<ServiceInstance> instanceList = discoveryClient.getInstances(serviceId);
			for (ServiceInstance instance : instanceList) {
				boolean isNew = true;
				String serverId = instance.getHost() + ":" + instance.getPort();
				for (ServerInfo servInfo : servInfoList) {
					if (servInfo.getId().equals(serverId)) {
						isNew = false;
						matchList.add(servInfo);
						if (!servInfo.getIsEnabled()) {
							servInfo.setIsEnabled(true);
							serverInfoService.update(servInfo);
						}
						break;
					}
				}
				//创建服务器信息
				if (isNew) {
					try {
						ServerInfo serverInfo = new ServerInfo();
						serverInfo.setId(serverId);
						serverInfo.setHost(instance.getHost());
						serverInfo.setPort(instance.getPort());
						serverInfo.setServiceId(serviceId);
						serverInfo.setIsEnabled(true);
						serverInfo.setWeight(0);
						serverInfoService.insert(serverInfo);
					} catch (Exception e) {
						logger.error("Insert ServerInfo Error", e);
					}
				}
			}
			//移除匹配后就是下线的服务器
			servInfoList.removeAll(matchList);
			if (servInfoList.size() > 0) {
				for (ServerInfo servInfo : servInfoList) {
					servInfo.setIsEnabled(false);
					serverInfoService.update(servInfo);
				}
			}
		}
		//把失效的服务设置为不可用
		if (map.size() > 0) {
			Iterator<ServiceInfo> itor = map.values().iterator();
			while(itor.hasNext()) {
				ServiceInfo serviceInfo = itor.next();
				serviceInfo.setIsEnabled(false);
				serviceInfoService.update(serviceInfo);
				
				List<ServerInfo> servInfoList = serverInfoService.getByServiceId(serviceInfo.getId());
				for (ServerInfo servInfo : servInfoList) {
					servInfo.setIsEnabled(false);
					serverInfoService.update(servInfo);
				}
			}
		}
	}
	
	public ServerInfo choose(RequestContext context) {
		// 检查是否已经加载
		if (serviceInfoMap.size() == 0) {
			synchronized (this) {
				load();
			}
		}
		String serviceId = (String) context.get(FilterConstants.SERVICE_ID_KEY);
		List<ServerInfo> list = serviceInfoMap.get(serviceId);
		if (list == null || list.size() == 0) {
			return null;
		}
		
		int totalWeight = 0;
		List<ServerInfo> matchList = new ArrayList<>();
		List<ServerInfo> policyList = new ArrayList<>();
		List<ServerInfo> normalList = new ArrayList<>();
		// 分别找出路由策略服务器和正常服务器
		for (ServerInfo servInfo : list) {
			if (servInfo.getIsEnabled() && servInfo.getWeight() > 0) {
				if (StringUtils.isNotEmpty(servInfo.getPolicyType())) {
					policyList.add(servInfo); 
				} else {
					normalList.add(servInfo);
				}
			}
		}
		// 检查符合路由策略的服务器
		if (policyList.size() > 0) {
			String userId = getUserId(context);
			String appName = getAppName(context);
			for (ServerInfo servInfo : policyList) {
				if (isMatch(servInfo, userId, appName)) {
					matchList.add(servInfo);
					totalWeight += servInfo.getWeight();
				}
			}
		} 
		// 没有匹配路由策略则使用正常服务器
		if (matchList.size() == 0) {
			for (ServerInfo servInfo : normalList) {
				totalWeight += servInfo.getWeight();
			}
			matchList = normalList;
		}
		// 根据设置的权重进行选择
		ServerInfo serverInfo = null;
		if (matchList.size() > 0) {
			Collections.sort(matchList);
			int randomWeight = random.nextInt(totalWeight);
			for (ServerInfo servInfo : matchList) {
				if (servInfo.getWeight() >= randomWeight) {
					serverInfo = servInfo;
					break;
				}
			}
		}
		return serverInfo;
	}
	
	private boolean isMatch(ServerInfo servInfo, String userId, String appName) {
		if (servInfo.getPolicyType().equals(RouteConstant.ROUTE_BY_APPNAME)) {
			if (appName != null && servInfo.getPolicyValue().contains(appName)) {
				return true;
			}
		} else if (servInfo.getPolicyType().equals(RouteConstant.ROUTE_BY_USERID)) {
			if (userId != null && servInfo.getPolicyValue().contains(userId)) {
				return true;
			}
		} else if (servInfo.getPolicyType().equals(RouteConstant.ROUTE_BY_USERAREA)) {
			if (userId != null) {
				UserInfo userInfo = userInfoService.getById(userId);
				if (userInfo != null && servInfo.getPolicyValue().contains(userInfo.getLocation())) {
					return true;
				}
			}
		}
		return false;
	}
	
	private String getUserId(RequestContext context) {
		HttpServletRequest request = context.getRequest();
//		return request.getHeader("userId");
		
		//测试用
		String[] arr = request.getRequestURI().split("/");
		String userId = arr[arr.length-1];
		return userId;
	}
	
	private String getAppName(RequestContext context) {
		HttpServletRequest request = context.getRequest();
		return request.getHeader("AppName");
	}
	
}
